#include <EIC.h>
#include <vcl.h>
#pragma hdrstop

#include <DBGrids.hpp>

#include "CForm.h"
#include "CTreeX.h"
#pragma package(smart_init)
#define MODULE_TAG  1000
//---------------------------------------------------------------------------
#ifdef _DEBUG
// Vérifie qu'il n'existe aucun bouton avec la propriété 'Default'=true
// car cela inhibe toutes les touches 'Entree' ce qui ne permet plus
// de passer de composant à composant avec cette touche.
void __fastcall VerifierButtons( TWinControl *pWndCtrl) {
  for( int i=0; i<pWndCtrl->ControlCount; i++) {
    if( dynamic_cast<TButton*>( pWndCtrl->Controls[i]) && (( TButton*)pWndCtrl->Controls[i])->Default) {
      CForm     *pForm = dynamic_cast<CForm*>( GetParentForm( pWndCtrl->Controls[i]));
      AnsiString strFormName;
      if( pForm) { ASSERT( pForm->TabEnter); strFormName = pForm->Name; }
      ::EICAttention( "Le bouton '" + pWndCtrl->Controls[i]->Name + "' possède sa propriété 'Default' à true ce qui est incompatible\navec la propriété 'TabEnter' de la form '" + strFormName + "' !");
    }
    if( dynamic_cast<TWinControl*>( pWndCtrl->Controls[i])) VerifierButtons(( TWinControl*)pWndCtrl->Controls[i]);
  }
}
#endif

//---------------------------------------------------------------------------
__fastcall CEICForm::CEICForm(HWND Owner) : TForm(Owner)
{
  m_bmpBackground = NULL;
  Init();
}



__fastcall CEICForm::CEICForm( TComponent* Owner /* = NULL */)
: TForm( Owner ? Owner : Screen->ActiveForm)
{
  m_bmpBackground = NULL;
  Init();
}


void __fastcall CEICForm::Init()
{
 m_bAutoCenterY  = true;
 m_bDataModified = false;
 m_bGridInsert   = true;
 m_ePosition     = posInitFromTForm;

 ASSERT(!m_bmpBackground);
 m_bmpBackground = new Graphics::TBitmap();

 ////////////////////////////////////////////
 // Détournement des propriétés redéfinies //
 ////////////////////////////////////////////
 m_OnPreview       = NULL;
 m_OnPrint         = NULL;
 m_OnCustomSelect  = NULL;
 m_OnComboDrawItem = NULL;

 // Création de la fiche
 //m_pfnOnDesignedCreate = inherited::OnCreate;
 //inherited::OnCreate   = OnInternalCreate;

 // Gestion des touches
 //m_OnKeyPress          = /*inherited::*/OnKeyPress;
 KeyPreview            = true;
 //inherited::OnKeyPress = EvOnKeyPress;

 // Par défaut ENTRER fait passer au composant suivant
 TabEnter = true;

 // Corrige le bug des grandes polices // CedricD le 04/04/2003
 Scaled = true;
 AutoScroll = false;
}

int __fastcall CEICForm::ShowModal()
{
  //Pour éviter qu'EICShell passe devant les fenêtres Builder
  if( hwndFenetreEICShell)
    EmpilerFenetre(Handle);
  return inherited::ShowModal();
}



__fastcall CEICForm::~CEICForm()
{
  if( hwndFenetreEICShell)
    DepilerFenetre(Handle);
  if (m_bmpBackground)
     {
     delete m_bmpBackground;
     m_bmpBackground = NULL;
     }
}

/******************************************************************************\
************* CForm::UpdateData() **********************************************
********************************************************************************
*                                                                              *
*    Cette fonction membre permet d'effectuer l'échange et la validation       *
* d'informations entre des controls et des variables. Elle se contente         *
* d'exécuter le membre virtuel d'échange de données de l'instance et de gérer  *
* une éventuelle validation infructueuse.                                      *
*                                                                              *
* Paramètres :                                                                 *
*   bUpdateForm : TRUE pour mettre à jour, à partir des variables d'échange,   *
*                 les controls de l'échange; FALSE pour faire l'inverse (i.e.  *
*                 lire les données des controls).                              *
*                                                                              *
* Retour :                                                                     *
*   TRUE si l'échange et la validation (effectuée seulement lorsque            *
* bUpdateForm = false) se sont bien passés; FALSE sinon (dans ce cas,          *
* l'échange peut ne pas s'être entièrement terminé).                           *
\******************************************************************************/
bool __fastcall CEICForm::UpdateData( bool bUpdateForm /* = false */)
{
 bool bOk;
 bOk = false;
 try {
   CDataExchange dx( *this, bUpdateForm);

   DoDataExchange( &dx);
   bOk = true;
 } catch( EUser& e) {
   // Une validation a échouée via dx.Fail()
   // => l'utilisateur en a été averti par un message approprié (on ne fait rien)
   ASSERT( !bOk);
 } catch( EConvertError& e) {
   // Une validation a échouée à cause d'une conversion (de Date via dx.Fail())
   // => l'utilisateur en a été averti par un message approprié (on ne fait rien)
   ASSERT( !bOk);
 } catch( Exception& e) {
   // Autre erreur
   // => Retransmise à l'appelant
   throw EUser( TAG( 10));
 }
 return bOk;
} /* CForm::UpdateData() */


//---------------------------------------------------------------------------
void __fastcall CEICForm::DoDataExchange(CDataExchange *pdx)
{
}


//---------------------------------------------------------------------------
void __fastcall CEICForm::SetPosition( enum FORM_POSITION ePosition /* = posInitFromTForm */)
{
 HWND      hwndParent; // Handle de la fenêtre parente
 RECT      rectParent; // Surface sur l'écran (en pixels) du parent de cette fenêtre
 TPosition poNew;      // Nouvelle position VCL de la fenêtre

 // Laisse la fenêtre se positionner toute seule
 if (ePosition==posInherrited) { m_ePosition = ePosition; return; }

 if( ePosition == posInitFromTForm)
 {
   switch( ((inherited*)this)->Position)
   { case poDefault      :
     case poScreenCenter : ePosition = posParentCenter; break;
     default             : ePosition = posDesigned;     break;
   }
 }

 if( ePosition != m_ePosition)
 { // Préparation des calculs
   switch( ePosition)
   { // Centrage dans le parent
     case posParentCenter :
       if( dynamic_cast<TForm*>( Owner) != NULL)
         hwndParent = dynamic_cast<TForm*>( Owner)->Handle;
       else if( ( hwndParent = ::GetParent( Handle)) == NULL)
       { // Pas de parent => On centre par rapport à la fenêtre principale de l'appli
         hwndParent = ( Application->MainForm) ? Application->MainForm->Handle : Application->Handle;
       }
       if( !::IsWindow( hwndParent) || ::IsIconic( hwndParent) || !::IsWindowVisible( hwndParent))
       { // Pas de parent
         // => On prend la fenêtre principale de l'appli ou l'écran
         if( !::IsWindow( hwndParent = ::GetForegroundWindow()))
           hwndParent = ::GetDesktopWindow();
       }
       ASSERT( ::IsWindow( hwndParent));
       poNew = poDesigned;
       break;

     // Centrage dans l'écran
     case posScreenCenter :
       hwndParent = ::GetDesktopWindow();
       poNew      = poScreenCenter;
       break;

     // Pas de correction de position nécessaire
     default :
       ASSERT( ePosition == posDesigned);
       poNew = poDesigned;
       break;
   }

   // Initialisations
   if( ( ePosition != posDesigned) && ::GetWindowRect( hwndParent, &rectParent))
   { // Coordonnées de centrage (centré horizontalement et au tiers de la hauteur du parent)
     if( ( hwndParent == Application->Handle) &&
         Application->MainForm &&
         ::IsRectEmpty( &rectParent))
     { // Le parent de cette fenêtre est une fenêtre vide
       // => On centre par rapport à la fenêtre principale de l'appli
       ::GetWindowRect( Application->MainForm->Handle, &rectParent);
     }

     ASSERT( ::IsWindow( hwndParent));
     if( !::IsRectEmpty( &rectParent))
     {
       TRect eSysScreen;

       ((TForm*)this)->Position = poNew;
       Left =                  (rectParent.right  + rectParent.left - Width ) / 2;
       Top  = rectParent.top + (rectParent.bottom - rectParent.top  - Height) / 3;

       ::SystemParametersInfo( SPI_GETWORKAREA, 0, &eSysScreen, 0);

       if( Left<eSysScreen.Left) Left = eSysScreen.Left;
       if( Top <eSysScreen.Top ) Top  = eSysScreen.Top;

       if( Top +Height>eSysScreen.Bottom) Top  = eSysScreen.Bottom - Height;
       if( Left+Width >eSysScreen.Right ) Left = eSysScreen.Right  - Width;
     }
   }

   // Stockage de la position de la fenêtre
   m_ePosition = ePosition;
 }
} /* CForm::SetPosition() */


//******************************************//
// Gestion interne des événements standards //
//******************************************//
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*\
+++++++++++++ CForm::OnInternalCreate() ++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+                                                                              +
+    Cette procédure permet d'initialiser les données affichées dans la fiche. +
+ C'est le gestionnaire interne d'initialisation d'une fiche.                  +
+                                                                              +
+ Note : A la première création de la fenêtre, les positions sont initialisées +
+        automatiquement en fonction du membre TForm::Position.                +
\*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void __fastcall CEICForm::AfterConstruction()
{
  TForm::AfterConstruction();

  SetPosition(m_ePosition);
  UpdateForm();
#ifdef _DEBUG
  if( TabEnter) VerifierButtons( this);
#endif
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*\
+++++++++++++ CForm::OnInternalKeyPress() ++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+                                                                              +
+    Cette procédure permet de filtrer les touches de l'utilisateur de façon à +
+ gérer la touche RETURN comme touche de passage d'un control à  l'autre.      +
\*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void __fastcall CEICForm::KeyPress( char &cKey)
{
  EvOnKeyPress( cKey);
  TForm::KeyPress( cKey);
}

void __fastcall CEICForm::EvOnKeyPress( /*TObject *Sender,*/ char &Key)
{
  // Filtrage du CTRL+E pour remplacer le € en E car le language des tables
  // paradox que l'on utilise ne l'acceptent pas
  if( Key=='€') Key = 'E';

  if(( Key==VK_TAB) && dynamic_cast< TCustomDBGrid*>( ActiveControl) && !m_bGridInsert) Key = VK_RETURN;

 // Filtrage de la touche RETURN
 if(( Key != VK_RETURN) || dynamic_cast<TMemo*>(ActiveControl))
 { // Toutes les autres touches sont acceptées
   // => Passage dans le gestionnaire créé à la conception de la fiche
//   if( m_OnKeyPress)
//     m_OnKeyPress(Sender, Key);
   return;
 }
 if (!TabEnter)
  return;

 // Annulation de la touche
 Key = 0;


 /////////////////////////////////
 // Gestion de la touche Entrée //
 /////////////////////////////////
  TCustomDBGrid* pDBGrid;
  TDataSet*      pDataSet;
  TField*        pfld;
  int            nNoColonne;
  int            nNbGridColFields;
  bool           bEof;

  //if( ( pDBGrid = dynamic_cast<TCustomDBGrid*>( ActiveControl)) == NULL)
  if((( pDBGrid = dynamic_cast<TCustomDBGrid*>( ActiveControl)) == NULL) || (( TDBGrid*)pDBGrid)->ReadOnly)
  { // Le champ courant n'est pas une grille
   // => On passe au control suivant
   Perform( WM_NEXTDLGCTL, 0, FALSE);
  }
  else
  {ASSERT((( TDBGrid*)pDBGrid)->DataSource);
   ASSERT((( TDBGrid*)pDBGrid)->DataSource->DataSet);
   // Le champ courant est une grille
   // => on se deplace à l'intérieur de la grille (Le cast en TDBGrid* permet seulement d'accéder aux propriétés protégées)
   bEof = ( pDataSet = (((TDBGrid*)pDBGrid)->DataSource)->DataSet)->Eof;
   if( ((TDBGrid*)pDBGrid)->Options.Contains( dgRowSelect))
   { // La grille est en mode d'affichage "par ligne"
     // => Passage à la ligne suivante
     if( bEof)
     { // On est à la fin de la grille => Touche invalide
       ::MessageBeep( 0xFFFFFFFF);
     }
     else
     { // Passage à la ligne suivante
       pDataSet->Next();
     }
   }
   else
   { // La grille est en mode d'affichage "par cellule"
     // => Changement de cellule (ou de ligne si on est sur la dernière cellule)
     if( ( nNoColonne = pDBGrid->SelectedIndex) < ( ( nNbGridColFields = pDBGrid->FieldCount) - 1))
     { // La colonne courante n'est pas la dernière de la ligne ...
       if( ( nNoColonne == 0) && ( pDataSet->State == dsBrowse))
       { // ... mais la ligne est peut-être vide et en consultation (cas d'une table vide en consultation i.e. pas encore modifiée/renseignée)
         while( --nNbGridColFields >= 0)
           if( ( pfld = pDBGrid->Fields[ nNbGridColFields]) != NULL)
             if( !pfld->IsNull)
             { // Cette ligne (en consultation pour l'instant) n'est pas vide
               // => Passage à la colonne suivante
               goto LineNotEmpty;
             }

         // Cette ligne est bien entièrement vide et en consultation
         // => Passage au control suivant (il ne sert à rien - à priori - de rester sur cette ligne)
         Perform( WM_NEXTDLGCTL, 0, FALSE);
         return;
       }

       LineNotEmpty:
       // Cette ligne n'est pas vide
       // => Changement de colonne
       nNoColonne++;
     }
     else
     { // La colonne courante est la dernière
       // => On va passer à la ligne suivante
lblUnbug:
       if( !bEof)
       { // On n'a pas encore dépassé la fin de la table associée
         // => Elément suivant de la table
         pDataSet->Next();
         if( pDataSet->Eof)
         { // On était sur le dernier élément de la table
           // => Passage en mode "Ajout" dans la table (une ligne vierge est ajoutée dans la grille)
           if( m_bGridInsert) pDataSet->Append();
                         else return;
         }
       }
       else
       { // La fin du dataset est dépassée
         // => Passage en mode "Ajout" dans la table (une ligne vierge est ajoutée dans la grille)
         if( m_bGridInsert) pDataSet->Append();
       }

       // Positionnement sur la première colonne de la nouvelle ligne active de la grille
       nNoColonne = 0;
     }

     // Changement de cellule (on ignore les cellules en "Lecture seule")
     // (sur une ligne, une cellule au moins, doit être "éditable" sinon la fonction plante...)
     ASSERT( nNoColonne < pDBGrid->FieldCount);
     while( ((TDBGrid*)pDBGrid)->Columns->Items[ nNoColonne]->ReadOnly)
     {
       nNoColonne++;
       if ( nNoColonne>=pDBGrid->FieldCount) { /*pDataSet->Cancel();*/ goto lblUnbug; }
       ASSERT( ((TDBGrid*)pDBGrid)->Columns->Items[ nNoColonne]);
     }
     pDBGrid->SelectedIndex = nNoColonne;
   }
  }
} /* CForm::OnInternalKeyPress() */
//---------------------------------------------------------------------------
void __fastcall CForm::WMEraseBackground( TMessage &eMessage)
{
  if(m_bmpBackground && !m_bmpBackground->Empty) {
    HDC hdcBitmap  = m_bmpBackground->Canvas->Handle;
    int nWidthBmp  = m_bmpBackground->Width;
    int nHeightBmp = m_bmpBackground->Height;

    for( int y=0; y<VertScrollBar->Position+ClientHeight; y+=nHeightBmp)
       for( int x=0; x<HorzScrollBar->Position+ClientWidth; x+=nWidthBmp)
          ::BitBlt(( HDC)eMessage.WParam, x-HorzScrollBar->Position, y-VertScrollBar->Position, nWidthBmp, nHeightBmp, hdcBitmap, 0, 0, SRCCOPY);
  } else
    TForm::Dispatch( &eMessage);
}
//---------------------------------------------------------------------------
void __fastcall CForm::RecentrerControls()
{
  int Gauche,Droite,Haut,Bas,DecallageX,DecallageY=0,Taille,Client_Width,Client_Height,Client_Top,Client_Left;
  int GaucheTmp, HautTmp;

  ASSERT(Position != poOwnerFormCenter);  //Problème de centrage de la fenêtre quand elle est affichée dans l'arbre

  Droite = Bas = Client_Top = Client_Left = 0;
  Client_Width = Gauche = ClientWidth;
  Client_Height = Haut = ClientHeight;
  for (int i = 0;i < ControlCount;i++)
    if (Controls[i]->Visible && (dynamic_cast<TImage *>(Controls[i]) == NULL))
      switch (Controls[i]->Align)
        {
          case alNone:
            if (Controls[i]->Left < Gauche)
              Gauche = Controls[i]->Left;
            if (Controls[i]->Left + Controls[i]->Width > Droite)
              Droite = Controls[i]->Left + Controls[i]->Width;
            if (Controls[i]->Top < Haut)
              Haut = Controls[i]->Top;
            if (Controls[i]->Top + Controls[i]->Height > Bas)
              Bas = Controls[i]->Top + Controls[i]->Height;
            break;
          case alRight:
            Client_Width -= Controls[i]->Width;
            break;
          case alLeft:
            Client_Width -= Controls[i]->Width;
            Client_Left += Controls[i]->Width;
            break;
          case alBottom:
            Client_Height -= Controls[i]->Height;
            break;
          case alTop:
            Client_Height -= Controls[i]->Height;
            Client_Top += Controls[i]->Height;
            break;
        }
  GaucheTmp = Gauche;
   HautTmp = Haut;
  if( Gauche<0) Gauche = 0;
  if( Haut  <0) Haut   = 0;
  Taille = Droite - Gauche;
  DecallageX = GaucheTmp/*Gauche*/ - (Taille < Client_Width ? (Client_Width - Taille) / 2 : 0) - Client_Left;
  switch (_CodePartenaire)
    {
      case ecp_Fiduciaire_DR:
        if (m_bAutoCenterY)
           DecallageY = Haut - Client_Top;
        break;
      default:
        Taille = Bas - Haut;
        if (m_bAutoCenterY)
           DecallageY = HautTmp - (Taille < Client_Height ? (Client_Height - Taille) / 3 : 0) - Client_Top;
    }
  for (int i = 0;i < ControlCount;i++)
    if ((Controls[i]->Align == alNone) && (dynamic_cast<TImage *>(Controls[i]) == NULL))
      {
        Controls[i]->Left -= DecallageX;
        Controls[i]->Top -= DecallageY;
      }
}
//---------------------------------------------------------------------------
void __fastcall CForm::SetTabEnter( bool bValue)
{
  m_bTabEnter = bValue;
}



void __fastcall CForm::AfficherPremiereFille()
{
  ASSERT( dynamic_cast< CTree*>( Owner));

  int nTagChild = (( CTree*)Owner)->GetFirstChild( SCREEN_TAG( this));
  if( nTagChild)  (( CTree*)Owner)->SetSelectedScreen( nTagChild);
}

//---------------------------------------------------------------------------
//////////////////////////
// Classe CDataExchange //
//////////////////////////
__fastcall CDataExchange::CDataExchange( CForm& frm, bool bUpdateForm)
: m_frm( frm)
{
 m_bUpdateForm         = bUpdateForm;
 m_pLastWinCtrl        = NULL;
 m_hwndLastCtrl        = NULL;
 m_bLastCtrlIsEditable = false;
 m_bFormIsCanceled     = ( m_frm.ModalResult == mrCancel);
}


/*************************************************************************\
************* CDataExchange::PrepareControl() *****************************
***************************************************************************
*                                                                         *
*    Cette fonction membre permet d'indiquer au contexte d'échange quel   *
* est le prochain control windows qui va être traité.                     *
*                                                                         *
* Paramètres :                                                            *
*   pWinCtrl : Un control window quelconque (qui n'accepte pas de saisie  *
*              de texte).                                                 *
*                                                                         *
* Retour :                                                                *
*   Le handle windows de la fenêtre du control                            *
\*************************************************************************/
HWND __fastcall CDataExchange::PrepareControl( TWinControl* pWinCtrl)
{
 ASSERT( pWinCtrl);


 /////////////////////////////////////////////////////////////////////////
 // Stockage du prochain control (qui n'accepte pas de saisie de texte) //
 /////////////////////////////////////////////////////////////////////////
 m_hwndLastCtrl        = ( m_pLastWinCtrl = pWinCtrl)->Handle;
 m_bLastCtrlIsEditable = false;
 return( m_hwndLastCtrl);
}


/*************************************************************************\
************* CDataExchange::PrepareEditableControl() *********************
***************************************************************************
*                                                                         *
*    Cette fonction membre permet d'indiquer au contexte d'échange quel   *
* est le prochain control windows qui va être traité.                     *
*                                                                         *
* Paramètres :                                                            *
*   pWinCtrl : Un control window quelconque (qui accepte une saisie de    *
*              texte).                                                    *
*                                                                         *
* Retour :                                                                *
*   Le handle windows de la fenêtre du control                            *
\*************************************************************************/
HWND __fastcall CDataExchange::PrepareEditableControl( TWinControl* pWinCtrl)
{
 ASSERT( pWinCtrl);

 ////////////////////////////////////////////////////////////////////
 // Stockage du prochain control (qui accepte une saisie de texte) //
 ////////////////////////////////////////////////////////////////////
 m_hwndLastCtrl        = ( m_pLastWinCtrl = pWinCtrl)->Handle;
 m_bLastCtrlIsEditable = true;
 return( m_hwndLastCtrl);
}



/******************************************************************************\
************* CDataExchange::Fail() ********************************************
********************************************************************************
*                                                                              *
*    Cette méthode (normalement appellée durant l'exécution d'une méthode      *
* CForm::DoDataExchange( false)) permet d'afficher un message de saisie        *
* erronée, d'activer le dernier control préparé dans le contexte et enfin de   *
* déclencher une exception EUser() destinée à stopper (via la méthode          *
* CForm::DoDataExchange()) l'échange dans le contexte.                         *
*                                                                              *
* Paramètres :                                                                 *
*   pcszErrBoxMsg : Message d'erreur (ou ids) à afficher.                      *
*                                                                              *
* Note :                                                                       *
*   Si la fiche dans lequel l'échange est réalisé est en cours d'annulation,   *
* aucun message d'erreur n'est affiché mais la propriété DataModified de la    *
* fiche est positionnée sur TRUE (cela permet d'indiquer, l'orsque la fiche va *
* être annulée, que des modifications ont eu lieu dans les controls de la      *
* fiche - une donnée erronée d'un control de la fiche est vue comme une modif).*
\******************************************************************************/
void __fastcall CDataExchange::Fail( const char* pcszErrBoxMsg)
{
 ASSERT( pcszErrBoxMsg);
 ASSERT( !m_bUpdateForm); // Normalement, Fail n'est appelé que pendant CForm::UpdateData( false)


 if( m_bFormIsCanceled)
 { // Fiche annulée //
   // On considère que des données ont été modifiées dans la fiche. Cela permet
   // d'indiquer qu'une donnée invalide (qui peut être affichée) n'est invalide
   // qu'à cause de l'utilisateur.
   DataModified = true;
 }
 else
 { // Affichage du message d'erreur //
   EICMsgBox( pcszErrBoxMsg, MAKEINTRESOURCE( IDS_DDX_FAIL_TITLE), MB_ICONEXCLAMATION | MB_OK | MB_TOPMOST);


   //////////////////////////////////////////////////////
   // Sélection du dernier control qui a posé problème //
   //////////////////////////////////////////////////////
   if( !m_bUpdateForm && ( m_hwndLastCtrl != NULL))
   { // Si le dernier control testé est dans une page à onglets, on réactive la page en question
     TWinControl* pwctrlParent;

     for( pwctrlParent = m_pLastWinCtrl->Parent; pwctrlParent; pwctrlParent = pwctrlParent->Parent)
       if( dynamic_cast<TTabSheet*>( pwctrlParent))
       { ASSERT( pwctrlParent->Parent && dynamic_cast<TPageControl*>( pwctrlParent->Parent));
         ((TPageControl*)pwctrlParent->Parent)->ActivePage = (TTabSheet*)pwctrlParent;
         break;
       }

     // Remet le focus sur le dernier control testé dans l'échange
     ::SetFocus( m_hwndLastCtrl);
     if( m_bLastCtrlIsEditable)
     { // Sélection de tout le texte du control
       ::SendMessage( m_hwndLastCtrl, EM_SETSEL, 0, MAKELPARAM( 0, -1));
     }
   }
 }

 // => Cette exception permet de sortir de la fonction de validation et de
 //    d'échange de la CForm
 throw EUser( TAG( 20));
} /* CDataExchange::Fail() */



/******************************************************************************\
************* DDX_IsChecked() **************************************************
********************************************************************************
*                                                                              *
*    Cette fonction d'échange de données dans un contexte permet de connaître  *
* ou initialiser l'état "Checked" d'une case à cocher.                         *
*                                                                              *
* Paramètres :                                                                 *
*   pdx  : Contexte d'échange fourni par CForm::DoDataExchange()               *
*   pchk : Une case à cocher                                                   *
*   b    : Valeur de la propriété "Checked" de la case à cocher.               *
*                                                                              *
* Retour :                                                                     *
*   Le paramètre b                                                             *
*                                                                              *
* ATTENTION: Lorsque l'état de la case à cocher est modifié par la fonction,   *
*            l'événement OnClick de la case à cocher N'EST PAS déclenché.      *
\******************************************************************************/
bool __fastcall DDX_IsChecked( CDataExchange*   pdx,
                               TCustomCheckBox* pchk,
                               bool&            b)
{
 HWND hWndChk; // Handle Windows de la fenêtre de la case à cocher


 // Echange de la marque //
 // Préparation du control
 hWndChk = pdx->PrepareControl( pchk);

 if( pdx->UpdateForm)
 { // Positionnement/Effacement de la marque sur la case à cocher
   if( ( ::SendMessage( hWndChk, BM_GETCHECK, 0, 0) == BST_CHECKED) ^ b)
   { // L'état de la case à cocher doit être modifié (L'EVENEMENT OnClick() va être déclenché)
     ((TCheckBox*)pchk)->Checked = b; // Cast pour permettre l'accès à Checked
   }
 }
 else
 { // Lecture de la marque de la case à cocher
   if( ( ::SendMessage( hWndChk, BM_GETCHECK, 0, 0) == BST_CHECKED) ^ b)
   { // La marque de la case à cocher à changée
     b ^= true;
     pdx->DataModified = true;
   }
 }


 return( b);
} /* DDX_IsChecked() */


/******************************************************************************\
************* DDX_Text() *******************************************************
********************************************************************************
*                                                                              *
*    Cette procédure d'échange de données dans un contexte permet de lire ou   *
* écrire le texte d'un control windows quelconque.                             *
*                                                                              *
* Paramètres :                                                                 *
*   pdx      : Contexte d'échange fourni par CForm::DoDataExchange()           *
*   pWinCtrl : Un control window quelconque                                    *
*   str      : Texte du control                                                *
\******************************************************************************/
AnsiString& __fastcall DDX_Text( CDataExchange* pdx,
                                 TWinControl*   pWinCtrl,
                                 AnsiString&    str)
{
 HWND hWndCtrl; // Handle Windows de la fenêtre du control

 ASSERT( pdx);

 // Echange du texte //
 // Préparation du control
 hWndCtrl = pdx->PrepareEditableControl( pWinCtrl);

 if( pdx->UpdateForm)
 { // Affichage du texte dans le control
   ::SetWindowText( hWndCtrl, str.c_str());
 }
 else
 { // Lecture du texte présent dans le control
   AnsiString strNewText; // Nouveau texte du control
   int        nLen;       // Longueur du texte à lire dans la fenêtre

   nLen = ::GetWindowTextLength( hWndCtrl);
   strNewText.SetLength( nLen);
   if( nLen > 0)
     ::GetWindowText( hWndCtrl, strNewText.c_str(), nLen + 1);

   if( strNewText != str)
   { // Le texte du control a changé
     str               = strNewText;
     pdx->DataModified = true;
   }
 }
 return( str);
} /* DDX_Text() */


/******************************************************************************\
************* DDX_Time() *******************************************************
********************************************************************************
*                                                                              *
*    Cette procédure d'échange de données dans un contexte permet de lire ou   *
* positionner l'heure dans un control windows quelconque.                      *
*                                                                              *
* Paramètres :                                                                 *
*   pdx      : Contexte d'échange fourni par CForm::DoDataExchange()           *
*   pWinCtrl : Un control window quelconque                                    *
*   time     : Heure affichée (ou lue) dans le control                         *
*   eFormat  : Format de l'heure (en lecture et écriture)                      *
*                                                                              *
* Notes : + Lorsque l'échange est une lecture du control, une validation       *
*           automatique sur le format de l'heure est réalisée.                 *
*         + L'éventuelle date qui pourrait exister dans l'heure passée en      *
*           paramètre est ignorée (et non modifiée) par cette procédure.       *
\******************************************************************************/
void __fastcall DDX_Time( CDataExchange* pdx,
                          TWinControl*   pWinCtrl,
                          TDateTime&     time)
{
 HWND hWndCtrl; // Handle Windows du control

 ASSERT( pdx && pWinCtrl);

 // Echange de l'heure //
 // Préparation du control
 hWndCtrl = pdx->PrepareEditableControl( pWinCtrl);

 if( pdx->UpdateForm)
 { // Affichage de l'heure
   ::SetWindowText( hWndCtrl, time.FormatString( "t").c_str());
 }
 else
 { // Lecture de l'heure
   #pragma message( "*** DDX_Time() non implémenté pour lire l'heure du control... ***" )
   #pragma option -w-ccc // Désactive le msg "Condition is always true"
   ASSERT( false);
   #pragma option -w.ccc
/*
   AnsiString strTime;
   int        nLen;

   nLen = ::GetWindowTextLength( hWndCtrl);
   strTime.SetLength( nLen);
   if( nLen > 0)
     ::GetWindowText( hWndCtrl, strTime.c_str(), nLen + 1);

   // Validation du format de l'heure
   ...
*/
 }
} /* DDX_Time() */


/******************************************************************************\
************* DDX_LBIndex() ****************************************************
********************************************************************************
*                                                                              *
*    Cette procédure d'échange de données dans un contexte permet de lire ou   *
* positionner la ligne courante d'une boîte à liste. Les lignes de la boîte à  *
* liste sont numérotées de 1 à N (0 signifie aucune ligne).                    *
*                                                                              *
* Paramètres :                                                                 *
*   pdx       : Contexte d'échange fourni par CForm::DoDataExchange()          *
*   plb       : Une boîte à liste                                              *
*   nIndex    : Index de la ligne de la boîte à liste                          *
*   nMinIndex : Index minimal accepté en lecture (ignoré si <= 0)              *
*   nMaxIndex :   "   maximal    "    "  lecture (ignoré si < nMinIndex)       *
*                                                                              *
* Retour (à l'usage des attributs déclarés en tant que propriété __property    *
* qui n'admettent pas les passages par référence) :                            *
*   Le n° de la ligne positionnée (ou lue)                                     *
\******************************************************************************/
int __fastcall DDX_LBIndex( CDataExchange*  pdx,
                            TCustomListBox* plb,
                            int&            nIndex,
                            int             nMinIndex /* = 0 */,
                            int             nMaxIndex /* = 0 */)
{
 HWND hWndCtrl;  // Handle Windows de la fenêtre de la liste
 int  nNewIndex; // Nouvel index de la liste


 ASSERT( pdx && plb);

 // Echange de l'index //
 // Préparation de la liste
 hWndCtrl = pdx->PrepareControl( plb);

 if( pdx->UpdateForm)
 { // Sélection d'un élément de la liste
   ::SendMessage( hWndCtrl, LB_SETCURSEL, (WPARAM)( nIndex - 1), 0L);
 }
 else
 { // Lecture de l'élément sélectionné dans la liste
   if( ( nNewIndex = (( int)::SendMessage( hWndCtrl, LB_GETCURSEL, 0, 0L) + 1)) != nIndex)
   { // Il y a eu modif de l'index dans la liste
     nIndex            = nNewIndex;
     pdx->DataModified = true;
   }

   // Validation de l'élément
   DDV_MinMaxListIndex( pdx, nIndex, nMinIndex, nMaxIndex);
 }
 return( nIndex);
} /* DDX_LBIndex() */


/******************************************************************************\
************* DDX_CBIndex() ****************************************************
********************************************************************************
*                                                                              *
*    Cette fonction d'échange de données dans un contexte permet de lire ou    *
* positionner la ligne courante d'une combo liste. Les lignes de la combo      *
* liste sont numérotées de 1 à N (0 signifie aucune ligne).                    *
*                                                                              *
* Paramètres :                                                                 *
*   pdx           : Contexte d'échange fourni par CForm::DoDataExchange()      *
*   pcb           : Une boîte combo liste                                      *
*   nIndex        : Index de la ligne de la boîte combo liste                  *
*   eMinIndex     : Valeur minimale associée à la 1ère ligne de la Combo.      *
*   eInvalidIndex : Valeur correspondant à aucune sélection dans la Combo.     *
*                                                                              *
* Retour (à l'usage des attributs déclarés en tant que propriété __property    *
* qui n'admettent pas les passages par référence) :                            *
*   Le n° de la ligne positionnée (ou lue)                                     *
\******************************************************************************/
int __fastcall DDX_CBIndex( CDataExchange*   pdx,
                            TCustomComboBox* pcb,
                            int&             nIndex,
                            int              eMinIndex /* = 0 */,
                            int              eInvalidIndex /* = -1 */)
{
 HWND hWndCtrl;  // Handle Windows de la fenêtre de la combo liste
 int  nNewIndex; // Nouvel index de la combo liste

 ASSERT( pdx && pcb);
 ASSERT( ( nIndex == eInvalidIndex) || ( ( nIndex - eMinIndex) >= 0));

 // Echange de l'index //
 // Préparation de la combo liste
 hWndCtrl = pdx->PrepareControl( pcb);

 if( pdx->UpdateForm)
 { // Sélection d'un élément de la combo liste
   ::SendMessage( hWndCtrl, CB_SETCURSEL, (WPARAM)( ( nIndex == eInvalidIndex) ? -1 : ( nIndex - eMinIndex)), 0L);
 }
 else
 { // Lecture de l'élément sélectionné dans la combo liste
   if( ( nNewIndex = (int)::SendMessage( hWndCtrl, CB_GETCURSEL, 0, 0L)) == -1)
     nNewIndex = eInvalidIndex;
   else
     nNewIndex += eMinIndex;
   if( nNewIndex != nIndex)
   { // Il y a eu modif de l'index dans la combo liste
     nIndex            = nNewIndex;
     pdx->DataModified = true;
   }
 }
 return( nIndex);
} /* DDX_CBIndex() */


/******************************************************************************\
************* DDX_RadioGrp() ***************************************************
********************************************************************************
*                                                                              *
*    Cette fonction d'échange de données dans un contexte permet de lire ou    *
* initialiser un bouton radio au sein d'un groupe de boutons radio.            *
*                                                                              *
* Paramètres :                                                                 *
*   pdx        : Contexte d'échange fourni par CForm::DoDataExchange()         *
*   prgrp      : Un groupe de boutons radio                                    *
*   nItemIndex : Index, dans le groupe de boutons radio, du bouton à lire/MAJ. *
*                                                                              *
* Retour (à l'usage des attributs déclarés en tant que propriété __property    *
* qui n'admettent pas les passages par référence) :                            *
*   Le n° du bouton radio initialisé (-1 = aucun)                              *
\******************************************************************************/
int __fastcall DDX_RadioGrp( CDataExchange* pdx,
                             TRadioGroup*   prgrp,
                             int&           nItemIndex)
{
 int  nNewItemIndex; // Nouveau n° du bouton radio sélectionné dans le groupe

 ASSERT( pdx && prgrp);

 // Echange de l'index //
 // Préparation du groupe de boutons radio
 pdx->PrepareControl( prgrp);

 if( pdx->UpdateForm)
 { // Sélection du bouton radio dans le groupe
   prgrp->ItemIndex = nItemIndex;
 }
 else
 { // Lecture du bouton radio activé dans le groupe
   if( ( nNewItemIndex = prgrp->ItemIndex) != nItemIndex)
   { // Il y a eu modif du douton radio sélectionné dans le groupe
     nItemIndex        = nNewItemIndex;
     pdx->DataModified = true;
   }
 }
 return( nItemIndex);
} /* DDX_RadioGrp() */


//*********************************************//
// Fonctions standard de validation de données //
//*********************************************//
/******************************************************************************\
************* DDV_MinStrLen() **************************************************
********************************************************************************
*                                                                              *
*    Cette procédure de validation d'un contexte d'échange permet de vérifier  *
* la longueur minimale d'une chaîne.                                           *
*                                                                              *
* Paramètres :                                                                 *
*   pdx     : Contexte d'échange fourni par CForm::DoDataExchange()            *
*   cstr    : Chaîne à tester                                                  *
*   nMinLen : Longueur minimale de la chaîne (ignorée si <= 0)                 *
*                                                                              *
* Note : La longueur n'est pas vérifiée si l'utilisateur abandonne la fiche et *
*        si la chaîne est vide.                                                *
\******************************************************************************/
void __fastcall DDV_MinStrLen( CDataExchange*    pdx,
                               const AnsiString& cstr,
                               int               nMinLen /* = 1 */)
{
 ASSERT( pdx);


 //////////////////////////////////////
 // Test de la longueur de la chaîne //
 //////////////////////////////////////
 if( !pdx->UpdateForm)
 { // Le contexte est bien positionné dans le sens "lecture de la fiche"
   if( ( nMinLen > 0) && ( cstr.Length() < nMinLen))
   { // La chaîne est trop courte (erreur si chaîne non vide ou Fiche validée)
     if( !cstr.IsEmpty() || !pdx->FormIsCanceled)
       pdx->Fail( EICFormat( ( nMinLen <= 1) ? MAKEINTRESOURCE( IDS_DDV_SAISIE_OBLIGATOIRE) :
                                               MAKEINTRESOURCE( IDS_DDV_AU_MOINS_Fmt_CARACTERES),
                             nMinLen
                           ).c_str());
   }
 }
} /* DDV_MinStrLen() */


void __fastcall DDV_DateNonVide( CDataExchange*    pdx,
                               const TDateTime &Date)
{
 ASSERT( pdx);

 if( !pdx->UpdateForm)
 { // Le contexte est bien positionné dans le sens "lecture de la fiche"
   if( (Date == TDateTime(0)) && !pdx->FormIsCanceled)
       pdx->Fail( "La date doit être renseignée.");
 }
} /* DDV_DateNonVide() */


/******************************************************************************\
************* DDV_MinMaxListIndex() ********************************************
********************************************************************************
*                                                                              *
*    Cette procédure de validation d'un contexte d'échange permet de vérifier  *
* l'étendue d'un index de liste.                                               *
*                                                                              *
* Paramètres :                                                                 *
*   pdx       : Contexte d'échange fourni par CForm::DoDataExchange()          *
*   nIndex    : Index à teester                                                *
*   nMinIndex : Index minimal accepté (ignoré si <= 0)                         *
*   nMaxIndex :   "   maximal    "    (ignoré si < nMinIndex)                  *
*                                                                              *
* Note : La vérification n'est pas effectuée si l'utilisateur est en train     *
*        d'abandonner la fiche et que l'index vaut zéro (i.e. aucun élément    *
*        sélectionné dans la liste).                                           *
\******************************************************************************/
void __fastcall DDV_MinMaxListIndex( CDataExchange* pdx,
                                     int            nIndex,
                                     int            nMinIndex /* = 0 */,
                                     int            nMaxIndex /* = 0 */)
{
 ASSERT( pdx);

 // Test de l'index //
 if( !pdx->UpdateForm)
 { // Le contexte est bien positionné dans le sens "lecture de la fiche"
   if( ( ( nMinIndex > 0) && ( nIndex < nMinIndex)) ||
       ( ( nMaxIndex > 0) && ( nMaxIndex >= nMinIndex) && ( nIndex > nMaxIndex)))
   { // L'index à tester est invalide (erreur si index positionné ou fiche validée)
     if( nIndex || !pdx->FormIsCanceled)
       pdx->Fail( ( nIndex <= 0) ? MAKEINTRESOURCE( IDS_DDV_SAISIE_OBLIGATOIRE) :
                                   MAKEINTRESOURCE( IDS_DDV_ELEMENT_DE_LISTE_INVALIDE));
   }
 }
} /* DDV_MinMaxListIndex() */
//---------------------------------------------------------------------------
namespace Cform
{
/*
    void __fastcall PACKAGE Register()
    {
        TComponentClass classes[1] = {__classid(CEICForm)};
        RegisterComponents("EIC Outils", classes, 0);
    }
*/
}



